using System;
using System.Windows.Forms;
using NUnit.Framework;
using NUnit.Extensions.Forms;
using Microsoft.VSPowerToys.PackInstaller;
using Microsoft.VSPowerToys.Common.TaskManager.ContentInstaller;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Diagnostics;
using System.Reflection;
using System.Text.RegularExpressions;

namespace GUITestScenarios
{
    [TestFixture]
    public class FeedFileTests : NUnitFormTest
    {
        #region Private Data 
        private PackInstallerForm packInstallForm;
        private ErrorMessages errorMessages;
        private string expectedMessage;
        private string downloadedFeedFileName;
        private string startDirectoryFeedFileName;

        #endregion

        [TestFixtureSetUp]
        public void Init()
        {
            Process[] visualStudioProcesses = Process.GetProcessesByName("devenv");
            TextWriter errorWriter = Console.Error;
            if (visualStudioProcesses.Length != 0)
            {
                errorWriter.WriteLine("You must close Visual Studio before running the test suite.");
                Console.WriteLine("You must close Visual Studio before running the test suite.");
                Assert.IsTrue(false);
            }
            DialogResult dialogResult = MessageBox.Show("Please disable or disconnect your network and then click OK.", "Disable Network", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);
            if (dialogResult != DialogResult.OK)
            {
                errorWriter.WriteLine("User clicked Cancel in response to Disable Network message.");
                Console.WriteLine("User clicked Cancel in response to Disable Network message.");
                Assert.AreEqual(DialogResult.OK, dialogResult);
            }
            downloadedFeedFileName = Path.Combine(Application.CommonAppDataPath, "ToolFeedAtom.xml");
            startDirectoryFeedFileName = Path.Combine(Application.StartupPath, "ToolFeedAtom.xml");
        }

        [TestFixtureTearDown]
        public void ReconnectNetwork()
        {
            MessageBox.Show("Please reconnect your network and then click OK.", "Enable Network", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        #region Feed File Location tests
        [Test]
        public void NoLocalFeedFileTest()
        {
            errorMessages = new ErrorMessages();
            RemoveFeedFiles();
            if (errorMessages.Count == 0)
            {
                expectedMessage = "The tool feed could not be downloaded or found locally.";
                packInstallForm = new PackInstallerForm("");
                ExpectModal("PackInstallerForm", "GenericFormHandler");
                packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(GenericPacksLoadedHandler);
                packInstallForm.ShowDialog();
            }
            errorMessages.printErrors("NoLocalFeedFileTest");
            Assert.AreEqual(0, errorMessages.Count, "NoLocalFeedFileTest failed. Check Console output for error messages.");
            RemoveFeedFiles();
        }

        [Test]
        public void NoFileByThisNameExistsTest()
        {
            errorMessages = new ErrorMessages();

            expectedMessage = "The tool feed could not be downloaded or found locally.";
            packInstallForm = new PackInstallerForm("NoFileByThisNameExists");
            ExpectModal("PackInstallerForm", "GenericFormHandler");
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(GenericPacksLoadedHandler);
            packInstallForm.ShowDialog();
 
            errorMessages.printErrors("NoFileByThisNameExistsTest");
            Assert.AreEqual(0, errorMessages.Count, "NoFileByThisNameExistsTest failed. Check Console output for error messages.");
        }

        [Test]
        public void StartUpFeedFileTest()
        {
            errorMessages = new ErrorMessages();
            RemoveFeedFiles();
            if (errorMessages.Count == 0)
            {
                File.Copy(TestStrings.GeneralTestPackFeedXml, startDirectoryFeedFileName);
                expectedMessage = "The list of tools could not be downloaded.  The last known good list will be used instead.";
                packInstallForm = new PackInstallerForm("");
                ExpectModal("PackInstallerForm", "GenericFormHandler");
                packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(GenericPacksLoadedHandler);
                packInstallForm.ShowDialog();
            }
            errorMessages.printErrors("StartUpFeedFileTest");
            Assert.AreEqual(0, errorMessages.Count, "StartUpFeedFileTest failed. Check Console output for error messages.");
            RemoveFeedFiles();
        }

        [Test]
        public void DownLoadedBeforeStartUpFeedFileTest()
        {
            errorMessages = new ErrorMessages();
            RemoveFeedFiles();
            if (errorMessages.Count == 0)
            {
                // Put a good feed file in the download location
                File.Copy(TestStrings.GeneralTestPackFeedXml, downloadedFeedFileName);

                // Put a bad feed file in the startup directory
                File.Copy(TestStrings.BadXML3FeedFile, startDirectoryFeedFileName);

                // If we don't get this message, it used the wrong feed file 
                expectedMessage = "The list of tools could not be downloaded.  The last known good list will be used instead.";
                packInstallForm = new PackInstallerForm("");
                ExpectModal("PackInstallerForm", "GenericFormHandler");
                packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(DownLoadedOverStartUpFeedFileHandler);
                packInstallForm.ShowDialog();
            }
            errorMessages.printErrors("DownLoadedOverStartUpFeedFileTest");
            Assert.AreEqual(0, errorMessages.Count, "DownLoadedOverStartUpFeedFileTest failed. Check Console output for error messages.");
            RemoveFeedFiles();
        }

        /// <summary>
        /// Verifies that the downloaded feed file takes precedence over a feed file in the startup directory.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void DownLoadedOverStartUpFeedFileHandler(object sender, EventArgs e)
        {
            // If we don't get the right message, it used the wrong feed file
            StatusStripTester ct = new StatusStripTester("statusStrip1", "PackInstallerForm");
            if (ct.Properties.Items[0].Text != expectedMessage)
            {
                errorMessages.Add("Expected message '" + expectedMessage + "'\nbut found '" + ct.Properties.Items[0].Text + "'");
            }

            ButtonTester buttonTester = new ButtonTester("exitButton", "PackInstallerForm");
            buttonTester.Click();
        }
        #endregion

        #region Bad Feed File tests
        /// <summary>
        /// This test contains a typo in the contributor.name start tag so that it does not match the end tag
        /// </summary>
        [Test]
        public void BadXMLStartTagTest()
        {
            errorMessages = new ErrorMessages();
            expectedMessage = "An error occurred loading the tool feed file. " + "There is an error in XML document (18, 8).";
            packInstallForm = new PackInstallerForm(TestStrings.BadXML1FeedFile);
            ExpectModal("PackInstallerForm", "GenericFormHandler");
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(GenericPacksLoadedHandler);
            packInstallForm.ShowDialog();

            errorMessages.printErrors("BadXMLStartTagTest");
            Assert.AreEqual(0, errorMessages.Count, "BadXMLStartTagTest failed. Check Console output for error messages.");
        }

        /// <summary>
        /// This test contains a typo in the end tag for the contributor.name property so that it does not match the start tag
        /// </summary>
        [Test]
        public void BadXMLEndTagTest()
        {
            errorMessages = new ErrorMessages();
            expectedMessage = "An error occurred loading the tool feed file. " + "There is an error in XML document (18, 36).";
            packInstallForm = new PackInstallerForm(TestStrings.BadXML2FeedFile);
            ExpectModal("PackInstallerForm", "GenericFormHandler");
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(GenericPacksLoadedHandler);
            packInstallForm.ShowDialog();

            errorMessages.printErrors("BadXMLEndTagTest");
            Assert.AreEqual(0, errorMessages.Count, "BadXMLEndTagTest failed. Check Console output for error messages.");
        }

        /// <summary>
        /// This test contains matching typos in both the start and end tags for the contributor.name property.
        /// This essentially means that there is a property that is not part of the schema.
        /// However, the name property is now required but can be null. 
        /// The lack of a name property is a legitimate situation so the 
        /// the misnamed property that is not part of the schema should be ignored.
        /// </summary>
        [Test]
        public void EmptyXMLNamePropertyTest()
        {
            errorMessages = new ErrorMessages();
            // The file should load successfully but, since the file is not downloaded,
            // there will be no message.
            expectedMessage = String.Empty;

            packInstallForm = new PackInstallerForm(TestStrings.BadXML3FeedFile);
            ExpectModal("PackInstallerForm", "GenericFormHandler");
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(SpecialPacksLoadedHandler);
            packInstallForm.ShowDialog();

            errorMessages.printErrors("MissingXMLNamePropertyTest");
            Assert.AreEqual(0, errorMessages.Count, "MissingXMLNamePropertyTest failed. Check Console output for error messages.");
        }

        /// <summary>
        /// This test replaces the title tag with matching start and end tags which are not part of the schema.
        /// Since title is a required property in the schema, this should create an error condition.
        /// </summary>
        [Test]
        public void BadXMLPropertyTest()
        {
            errorMessages = new ErrorMessages();
            expectedMessage = "An error occurred loading the tool feed file. " + "There is an error in XML document (20, 6).";
            packInstallForm = new PackInstallerForm(TestStrings.BadXML4FeedFile);
            ExpectModal("PackInstallerForm", "GenericFormHandler");
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(GenericPacksLoadedHandler);
            packInstallForm.ShowDialog();

            errorMessages.printErrors("BadXMLTagTest");
            Assert.AreEqual(0, errorMessages.Count, "BadXMLTagTest failed. Check Console output for error messages.");
        }

        #endregion

        #region Bad URI and hidden flag tests
        string[] missingToolNames = 
                        { 
                          "VSCommand Shell", "MSBee",
                          "Managed Stack Explorer",
                          "Microsoft Best Practices Analyzer", 
                          "XML Notepad 2007", 
                          "TFS Administration Tool",
                        };
        string[] missingPackNames = { "Tools and Utilities", };

        /// <summary>
        /// Tests various invalid URI in home page field - tools with errors should not
        /// be displayed
        /// 1. blank
        /// 2. relative file
        /// 3. UNC path
        /// 4. formats declared invalid by Uri.IsWellFormedUriString
        /// 
        /// MSBee should not be displayed because the Feed file includes a flag that
        /// says to hide it.
        /// 
        /// </summary>
        [Test]
        public void BadHomePageURITest()
        {
            errorMessages = new ErrorMessages();

            packInstallForm = new PackInstallerForm(TestStrings.BadURIFeedFile);
            ExpectModal("PackInstallerForm", "GenericFormHandler");
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(BadURIHandler);
            packInstallForm.ShowDialog();

            errorMessages.printErrors("BadHomePageURITest");
            Assert.AreEqual(0, errorMessages.Count, "BadHomePageURITest failed. Check Console output for error messages.");
        }

        /// <summary>
        /// Tests various invalid URI in set up link field - tools with errors should not
        /// be displayed
        /// 1. blank
        /// 2. relative file
        /// 3. UNC path
        /// 4. formats declared invalid by Uri.IsWellFormedUriString
        /// 
        /// MSBee should not be displayed because the Feed file includes a flag that
        /// says to hide it.
        /// 
        /// </summary>
        [Test]
        public void BadSetupURITest()
        {
            errorMessages = new ErrorMessages();

            packInstallForm = new PackInstallerForm(TestStrings.BadURIFeedFile2);
            ExpectModal("PackInstallerForm", "GenericFormHandler");
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(BadURIHandler);
            packInstallForm.ShowDialog();

            errorMessages.printErrors("BadSetupURITest");
            Assert.AreEqual(0, errorMessages.Count, "BadSetupURITest failed. Check Console output for error messages.");
        }

        /// <summary>
        /// Tests various invalid URI in license agreement link field - tools with errors should not
        /// be displayed
        /// 1. blank
        /// 2. relative file
        /// 3. UNC path
        /// 4. formats declared invalid by Uri.IsWellFormedUriString
        /// 
        /// MSBee should not be displayed because the Feed file includes a flag that
        /// says to hide it.
        /// 
        /// </summary>
        [Test]
        public void BadLicenseURITest()
        {
            errorMessages = new ErrorMessages();

            packInstallForm = new PackInstallerForm(TestStrings.BadURIFeedFile3);
            ExpectModal("PackInstallerForm", "GenericFormHandler");
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(BadURIHandler);
            packInstallForm.ShowDialog();

            errorMessages.printErrors("BadLicenseURITest");
            Assert.AreEqual(0, errorMessages.Count, "BadLicenseURITest failed. Check Console output for error messages.");
        }

        public void BadURIHandler(object sender, EventArgs e)
        {

            // Verify that a pack that had all its tools discarded is not displayed itself
            foreach (string packname in missingPackNames)
            {
                CheckBoxTester installPackCheckBoxTester = new CheckBoxTester(packname + ".headerPanel.installPackCheckBox");

                try
                {
                    // If this comparison throws an exception or Visible is false, the control is not displayed
                    if (installPackCheckBoxTester.Properties.Visible != false)
                    {
                        errorMessages.Add("Pack '" + packname + "' was unexpectedly displayed.");
                    }
                }
                catch (NoSuchControlException)
                {
                    // Do nothing; The test passes
                }
            }

            foreach (string toolName in missingToolNames)
            {
                try
                {
                    CheckBoxTester toolCheckBoxTester = new CheckBoxTester(toolName + ".layoutPanel.installToolCheckBox", "PackInstallerForm");
                    // This should throw an exception is the control was not added to the pack list
                    // If the control was added, it should not be visible
                    if (toolCheckBoxTester.Properties.Visible != false)
                    {
                        errorMessages.Add("Tool '" + toolName + "' was unexpectedly displayed.");
                    }
                }
                catch (NoSuchControlException )
                {
                    // Do nothing; The test passes
                }
            }

            ButtonTester buttonTester = new ButtonTester("exitButton", "PackInstallerForm");
            buttonTester.Click();
        }

        #endregion

        #region Cannot download license tests - here because the network is disabled for this suite

        [Test]
        public void CannotDownloadLicenseTestClickClose()
        {
            errorMessages = new ErrorMessages();
            packInstallForm = new PackInstallerForm(TestStrings.GeneralTestPackFeedXml);
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(DownloadLicenseTestHandler);
            ExpectModal("PackInstallerForm", "GenericFormHandler");

            // Need an ExpectModal to handle the license download form.
            // If we can't download the license we do NOT expect any other forms to be displayed
            // so we intentionally do not put an ExpectModal for them. Then, if one is displayed 
            // the test will fail due to the unexpected dialog form showing up. The ExpectModal
            // is in the main test logic so we can use the same OnPacksLoaded 
            // callback (i.e. DownloadLicenseTestHandler) for all the can't download license tests
            ExpectModal("LicenseAgreementDownloadForm", "ClickLicenseFormCloseFormHandler");

            packInstallForm.ShowDialog();
            errorMessages.printErrors("CannotDownloadLicenseTestClickClose");
            Assert.AreEqual(0, errorMessages.Count, "CannotDownloadLicenseTestClickClose failed. Check Console output for error messages.");
        }

        private void DownloadLicenseTestHandler(object sender, EventArgs e)
        {
            ButtonTester installSoftwareButtonTester = new ButtonTester("installSoftwareButton", "PackInstallerForm");
            PanelControlTester packListControlTester = new PanelControlTester("packListPanel", "PackInstallerForm");

            // Check the box to install the Managed Stack Explorer
            CheckBoxTester ManagedStackExplorertoolCheckBoxTester = new CheckBoxTester("Tools and Utilities.toolsPanel.Managed Stack Explorer.layoutPanel.installToolCheckBox", "PackInstallerForm");
            ManagedStackExplorertoolCheckBoxTester.Click();

            // Verify that the Install button is now enabled
            if (installSoftwareButtonTester.Properties.Enabled != true)
            {
                errorMessages.Add("Install software button is disabled after one tool was selected.");
            }

            installSoftwareButtonTester.Click();

            // Should return here after the license download form is closed.
            ButtonTester exitButtonTester = new ButtonTester("exitButton", "PackInstallerForm");
            exitButtonTester.Click();
        }

        private void ClickLicenseFormCloseFormHandler()
        {
            ButtonTester buttonTester = new ButtonTester("closeButton", "LicenseAgreementDownloadForm");

            // Check the text of the close button
            if (buttonTester.Text != "&Close")
            {
                errorMessages.Add("License Agreement Download Form close button text is wrong: '" + buttonTester.Text + "'");
            }
            if (buttonTester.Properties.Visible == false)
            {
                errorMessages.Add("License Agreement Download Form close button is not visible");
            }
            buttonTester.Click();
        }

        [Test]
        public void CannotDownloadLicenseTestClickEnter()
        {
            errorMessages = new ErrorMessages();
            packInstallForm = new PackInstallerForm(TestStrings.GeneralTestPackFeedXml);
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(DownloadLicenseTestHandler);
            ExpectModal("PackInstallerForm", "GenericFormHandler");
            ExpectModal("LicenseAgreementDownloadForm", "ClickLicenseFormAcceptButtonHandler");
            packInstallForm.ShowDialog();
            errorMessages.printErrors("CannotDownloadLicenseTestClickEnter");
            Assert.AreEqual(0, errorMessages.Count, "CannotDownloadLicenseTestClickEnter failed. Check Console output for error messages.");
        }

        private void ClickLicenseFormAcceptButtonHandler()
        {
            FormTester formTester = new FormTester("LicenseAgreementDownloadForm");
            if (formTester.Properties.AcceptButton != null)
            {
                formTester.Properties.AcceptButton.PerformClick();
            }
            else
            {
                formTester.Close();
            }
        }

        [Test]
        public void CannotDownloadLicenseTestCloseForm()
        {
            errorMessages = new ErrorMessages();
            packInstallForm = new PackInstallerForm(TestStrings.GeneralTestPackFeedXml);
            packInstallForm.OnPacksLoaded += new EventHandler<EventArgs>(DownloadLicenseTestHandler);
            ExpectModal("PackInstallerForm", "GenericFormHandler");
            ExpectModal("LicenseAgreementDownloadForm", "CloseLicenseFormHandler");
            packInstallForm.ShowDialog();
            errorMessages.printErrors("CannotDownloadLicenseTestCloseForm");
            Assert.AreEqual(0, errorMessages.Count, "CannotDownloadLicenseTestCloseForm failed. Check Console output for error messages.");
        }

        private void CloseLicenseFormHandler()
        {
            FormTester formTester = new FormTester("LicenseAgreementDownloadForm");
            formTester.Close();
        }
        #endregion

        #region Private Methods
        private void SpecialPacksLoadedHandler(object sender, EventArgs e)
        {
            StatusStripTester ct = new StatusStripTester("statusStrip1", "PackInstallerForm");
            if (ct.Properties.Items[0].Text != expectedMessage)
            {
                errorMessages.Add("Expected message \n\t'" + expectedMessage + "'\nbut found \n\t'" + ct.Properties.Items[0].Text + "'");
            }
            ToolSummaryControlTester toolTester = new ToolSummaryControlTester("Managed Stack Explorer", "PackInstallerForm");
            if (toolTester.Properties.ToolData.MicrosoftProvidedTool != false)
            {
                errorMessages.Add("MicrosoftProvidedTool != false " );
            }

            ButtonTester buttonTester = new ButtonTester("exitButton", "PackInstallerForm");
            buttonTester.Click();
        }

        private void GenericPacksLoadedHandler(object sender, EventArgs e)
        {
            StatusStripTester ct = new StatusStripTester("statusStrip1", "PackInstallerForm");
            if (ct.Properties.Items[0].Text != expectedMessage)
            {
                errorMessages.Add("Expected message \n\t'" + expectedMessage + "'\nbut found \n\t'" + ct.Properties.Items[0].Text + "'");
            }
            ButtonTester buttonTester = new ButtonTester("exitButton", "PackInstallerForm");
            buttonTester.Click();
        }

        private void GenericFormHandler()
        {
        }

        private void RemoveFeedFiles()
        {
            if (File.Exists(downloadedFeedFileName))
            {
                File.SetAttributes(downloadedFeedFileName, (File.GetAttributes(downloadedFeedFileName) & (~FileAttributes.ReadOnly)));
                File.Delete(downloadedFeedFileName);
            }
            if (File.Exists(downloadedFeedFileName))
            {
                errorMessages.Add("Did not delete '" + downloadedFeedFileName + "'");
            }
            if (File.Exists(startDirectoryFeedFileName))
            {
                File.SetAttributes(startDirectoryFeedFileName, (File.GetAttributes(startDirectoryFeedFileName) & (~FileAttributes.ReadOnly)));
                File.Delete(startDirectoryFeedFileName);
            }
            if (File.Exists(startDirectoryFeedFileName))
            {
                errorMessages.Add("Could not delete '" + startDirectoryFeedFileName + "'");
            }
        }
        #endregion

    }
}
